package com.supreme.studyserver.component;

import com.supreme.commontools.FilePathUtils;
import com.supreme.studyserver.config.ScopeConfig;
import com.supreme.studyserver.entity.Computer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.nio.file.Path;

/**
 * bean生命周期
 */
@Component(value = "qumBeanMan")
public class BeanLifecycle implements
        BeanNameAware,
        BeanFactoryAware,
        ApplicationContextAware,
        BeanPostProcessor,
        InitializingBean,
        BeanDefinitionRegistryPostProcessor,
        BeanFactoryPostProcessor,
        SmartInitializingSingleton,
        CommandLineRunner,
        ApplicationRunner,
        DisposableBean {

    Path beanLifecycle = FilePathUtils.getPath("/temp-data/bean-lifecycle.log");

    public BeanLifecycle() {
        FilePathUtils.createFile(beanLifecycle);
    }

    @PostConstruct
    private void initBeanLifecycle() {
        FilePathUtils.appendWriteFile(beanLifecycle, "initBeanLifecycle");
    }

    @PreDestroy
    private void destroyBeanLifecycles() {
        FilePathUtils.appendWriteFile(beanLifecycle, "destroyBeanLifecycles");
    }

    /**
     * BeanNameAware
     * @param beanName
     */
    @Override
    public void setBeanName(@NonNull String beanName) {
        FilePathUtils.writeFile(beanLifecycle, "BeanNameAware setBeanName: " + beanName);
    }

    /**
     * BeanFactoryAware
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
        FilePathUtils.appendWriteFile(beanLifecycle, "BeanFactoryAware setBeanFactory");
    }

    /**
     * ApplicationContextAware
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        FilePathUtils.appendWriteFile(beanLifecycle, "ApplicationContextAware setApplicationContext");
    }

    /**
     * InitializingBean
     * 为bean提供了初始化方法的方式，它只包括afterPropertiesSet方法，凡是继承该接口的类，在初始化bean的时候都会执行该方法。这个扩展点的触发时机在postProcessAfterInitialization之前。
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        FilePathUtils.appendWriteFile(beanLifecycle, "InitializingBean afterPropertiesSet");
    }

    /**
     * BeanDefinitionRegistryPostProcessor
     * 在读取项目中的beanDefinition之后执行
     * 可以在这里动态注册自己的beanDefinition，可以加载classpath之外的bean
     * @param beanDefinitionRegistry
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        FilePathUtils.appendWriteFile(beanLifecycle, "BeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry");
    }

    /**
     * BeanFactoryPostProcessor
     * 调用时机在spring在读取beanDefinition信息之后，实例化bean之前。
     * 用户可以通过实现这个扩展接口来自行处理一些东西，比如修改已经注册的beanDefinition的元信息。
     * @param configurableListableBeanFactory
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        configurableListableBeanFactory.createBean(Computer.class);

//        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
//        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(BeanUser.class);
//        beanDefinitionBuilder.addPropertyValue("name", "苏三说技术");
//        defaultListableBeanFactory.registerBeanDefinition("beanUserMan", beanDefinitionBuilder.getBeanDefinition());

        FilePathUtils.appendWriteFile(beanLifecycle, "BeanFactoryPostProcessor postProcessBeanFactory");
    }

    /**
     * SmartInitializingSingleton
     * 在spring容器管理的所有单例对象（非懒加载对象）初始化完成之后调用的回调接口。其触发时机为postProcessAfterInitialization之后。
     */
    @Override
    public void afterSingletonsInstantiated() {
        FilePathUtils.appendWriteFile(beanLifecycle, "SmartInitializingSingleton afterSingletonsInstantiated");
    }

    /**
     * ApplicationRunner
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        FilePathUtils.appendWriteFile(beanLifecycle, "ApplicationRunner run");
    }

    /**
     * CommandLineRunner
     * @param args
     * @throws Exception
     */
    @Override
    public void run(String... args) throws Exception {
        FilePathUtils.appendWriteFile(beanLifecycle, "CommandLineRunner run");
    }

    /**
     * DisposableBean
     * spring容器销毁前，会调用该destroy方法，做一些额外的工作。
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        FilePathUtils.appendWriteFile(beanLifecycle, "DisposableBean destroy");
    }
}
